﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Threading;
using System.Text.RegularExpressions;
using IGolovkine.Core;
using IGolovkine.Core.Diagnostics;

namespace IGolovkine.ShoutCastRipper
{
    public class StationClient : IStationClient
    {
        public event EventHandler<StationClientContentEndEventArgs> ContentEnd;
        public event EventHandler<StationClientContentStartEventArgs> ContentStart;
        public event EventHandler<StationClientDisconnectedEventArgs> Disconnected;
        public event EventHandler<StationClientContentReceivedEventArgs> ContentReceived;

        private readonly TypeFactory<IStationConnection, string> _connectionFactory;

        private string _currentTitle;

        private DateTime _connectionTime = DateTime.MaxValue;
        private IStationConnection _connection;
        private Thread _recordingThread;

        private readonly ObjectDisposedCheck _disposedCheck;
        private readonly object _connectionLock = new object();

        public StationClient(TypeFactory<IStationConnection, string> connectionFactory)
        {
            Debug.NotDefault(() => connectionFactory);
            _connectionFactory = connectionFactory;
            _recordingThread = null;
            _disposedCheck = new ObjectDisposedCheck(this);
        }

        private volatile bool _isConnected;
        public bool IsConnected
        {
            get { return _isConnected; }
        }

        public void Connect(string url)
        {
            if (_isConnected)
            {
                throw new InvalidOperationException(string.Format("{0} is already connected", this.GetType().Name));
            }
            else
            {
                lock (_connectionLock)
                {
                    _connection = this._connectionFactory(url);
                    _connectionTime = DateTime.Now;
                    _recordingThread = new Thread(this.ReadContent);
                    _recordingThread.Name = "Recording Thread";
                    _isConnected = true;
                    _recordingThread.Start();
                }
            }
        }

        public void Disconnect()
        {
            if (_isConnected)
            {
                lock (_connectionLock)
                {
                    _connection.Close();
                    _connection = null;
                    _isConnected = false; 
                    _recordingThread = null;
                }
            }
            else
            {
                throw new InvalidOperationException(string.Format("{0} is already disconnected", this.GetType().Name));
            }
        }

        public string CurrentTitle
        {
            get { return _currentTitle; }
        }

        public DateTime ConnectionTime
        {
            get { return _connectionTime; }
        }

        private void ReadContent()
        {
            Exception exception = null;
            try
            {
                byte[] data = null;
                string contentTitle = null;
                while (true)
                {
                    lock (_connectionLock)
                    {
                        if (!_isConnected) break;
                        data = _connection.ReadContentData();
                        contentTitle = _connection.CurrentContentTitle;
                    }
                    this.ProcessData(data, contentTitle);
                }
            }
            catch (Exception ex)
            {
                exception = ex;
            }

            if (_currentTitle != null)
            {
                this.OnContentEnd(new StationClientContentEndEventArgs(_currentTitle, !this.IsConnected));
            }

            this.OnDisconnected(new StationClientDisconnectedEventArgs(_currentTitle, exception));
            if (_isConnected)
            {
                this.Disconnect();
            }
            this.ResetCurrentTitleState();
        }

        private void ProcessData(byte[] data, string title)
        {
            if (_currentTitle != title)
            {
                if (_currentTitle != null)
                {
                    this.OnContentEnd(new StationClientContentEndEventArgs(title, false));
                }

                if (_isConnected)
                {
                    _currentTitle = title;
                    this.OnStartContent(new StationClientContentStartEventArgs(title));
                }
            }

            if (_isConnected)
            {
                this.OnDataReceived(new StationClientContentReceivedEventArgs(data, title));
            }
        }

        private void ResetCurrentTitleState()
        {
            _connectionTime = DateTime.MaxValue;
            _currentTitle = null;
        }

        protected virtual void OnStartContent(StationClientContentStartEventArgs args)
        {
            if (this.ContentStart != null)
            {
                this.ContentStart(this, args);
            }
        }

        protected virtual void OnContentEnd(StationClientContentEndEventArgs args)
        {
            if (this.ContentEnd != null)
            {
                this.ContentEnd(this, args);
            }
        }

        protected virtual void OnDisconnected(StationClientDisconnectedEventArgs args)
        {
            if (this.Disconnected != null)
            {
                this.Disconnected(this, args);
            }
        }

        protected virtual void OnDataReceived(StationClientContentReceivedEventArgs args)
        {
            if (this.ContentReceived != null)
            {
                this.ContentReceived(this, args);
            }
        }

        public void Dispose()
        {
            _disposedCheck.Dispose();
            if (_isConnected)
            {
                this.Disconnect();
            }
        }
    }
}
